package com.dalianpai.gulimall.seckill.scheduled;

import com.dalianpai.gulimall.seckill.service.SeckillService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

/**
 * @create 2020/8/17 -- 22:08
 */
@Slf4j
@Service
public class SeckillSkuScheduled {

    @Autowired
    SeckillService seckillService;

    @Autowired
    RedissonClient redissonClient;

    private final String upload_lock = "seckill:upload:lock";

    /**
     * 1、 spring中的cron表达式只允许6位，quartz中的第7位的年是不允许写的
     * 2、 spring中的cron表达式中周的位置1-7表示周一到周日，quartz中周的位置1-7表示周日到周六
     * 0 3/30 * * * ?
     * 2021-02-19 23:03:00
     * 2021-02-19 23:33:00
     * 2021-02-20 00:03:00
     *
     * 3、一个定时任务还没执行完，下一个定时任务就到执行的点了
     *    默认是会阻塞的，等待前面的定时任务执行完才执行下一次的
     *    解决办法：
     *      1、自己创建线程池，把要执行的任务丢到线程池中去执行
     *      CompletableFuture.runAsync(
     *          ()->{
     *              xxxService.bussiness();
     *          },executor
     *      );
     *      2、使用springboot中自带的异步任务
     *      类上面增加@EnableAsync  方法上面增加@Async
     *
     *  4、每天晚上3点业务不忙的时候执行定时，上架最近3天的秒杀商品(最近3天就可以有预告功能)
     *      秒  分  时  日  月  周
     */
    @Scheduled(cron ="0 * 3 * * ?")
    public void uploadSeckillSkulatest3Days(){
        log.info("商品上架");
        //获得锁的机器在执行下面的上架代码，上架代码内部要是幂等性的，其他机器获得锁后执行上架应该是没问题的，上架完后就不用上架了
        RLock lock = redissonClient.getLock(upload_lock);
        lock.lock(10, TimeUnit.SECONDS);//锁住10秒中
        try{
            //上架最近3天内秒杀的商品，重复上架的不需要处理
            //锁的业务执行完成，状态更新完成，释放锁后，其他机器在获得锁就可以在最新状态上面执行，这样就保证了这个业务接口的幂等性
            seckillService.uploadSeckillSkuLatest3Days();
        }finally {
            lock.unlock();//10秒后无论成功与否都进行解锁
        }

    }

}
